import { useMemo, useState } from "react";
import { ACTON_LIST, OPERATOR_LIST, OPERATOR_MAP, NUM_LIST } from "./constans";
import { pow, bignumber, divide, evaluate, round, format } from "mathjs";
import "./index.scss";

function isOperator(params) {
  return OPERATOR_LIST.find((operator) => params === operator.text);
}

export default function Calculator() {
  const [showEquation, setshowEquation] = useState(false);
  const [equation, setEquation] = useState(["0"]);
  const [isEval, setIsEval] = useState(false);

  const numList = NUM_LIST.map((item) => (
    <div
      key={item}
      className={`button ${item ? "" : "span-2"}`}
      onClick={() => handleNum(`${item}`)}
    >
      {item}
    </div>
  ));

  function getEnd(_equation) {
    const end = _equation.length - 1;
    const endItem = _equation[end];
    const endWithOperator = isOperator(endItem);
    return { end, endItem, endWithOperator };
  }

  function handleNum(num) {
    setshowEquation(true);
    if (isEval) {
      setIsEval(false);
      setEquation([num === "." ? `0${num}` : num]);
      return;
    }
    const { endWithOperator } = getEnd(equation);
    // 前方是运算符，直接pushNum
    if (endWithOperator) {
      setEquation(equation.concat(num));
    } else {
      // 数字处理
      const end = equation.length - 1;
      const endNum = equation[end];
      // 只能有一个小数点，百分号后不能加小数点
      if ((endNum.includes(".") || endNum.includes("%")) && num === ".") {
        return;
      }
      // 数字不以0开头
      const newNum = endNum === "0" && num !== "." ? num : endNum + num;
      const newEquaTion = equation.slice(0, end).concat(newNum);
      setEquation(newEquaTion);
    }
  }

  const actionList = ACTON_LIST.map((item) => (
    <div
      className="button"
      key={item.key}
      onClick={() => switchAction(item.key)}
    >
      <span>{item.text}</span>
    </div>
  ));

  function switchAction(key) {
    switch (key) {
      case "clear":
        handleClear();
        break;
      case "reciprocal":
        handleReciprocal();
        break;
      case "percent":
        handlePercent();
        break;
      default:
        break;
    }
  }

  function handleClear() {
    setshowEquation(false);
    setEquation(["0"]);
  }

  function getEquation(_equation) {
    setIsEval(false);
    const str = `${computedResult}`;
    if (Number(computedResult) < 0) {
      return [str];
    } else if (/e/i.test(str)) {
      const big = bignumber(computedResult);
      const res = format(big);
      return [res];
    } else return [str];
  }

  function handleReciprocal() {
    let _equation = [...equation];
    if (isEval) {
      if (isNaN(computedResult)) return;
      _equation = getEquation(_equation);
    }
    const { end, endItem, endWithOperator } = getEnd(_equation);
    if (endWithOperator) return;
    let newNum;
    if (endItem.includes("(-")) {
      newNum = endItem.replace("(-", "").replace(")", "");
    } else {
      newNum = `(-${endItem})`;
    }
    const newEquaTion = _equation.slice(0, end).concat(newNum);
    setEquation(newEquaTion);
    setshowEquation(true);
  }

  function handlePercent() {
    let _equation = [...equation];
    if (isEval) {
      if (isNaN(computedResult)) return;
      _equation = getEquation(_equation);
    }
    const { end, endItem, endWithOperator } = getEnd(_equation);
    if (endWithOperator) return;
    if (endItem.includes("%")) return;
    let newNum;
    if (endItem.includes("e") && !endItem.includes("(-")) {
      newNum = `(${endItem})%`;
    } else {
      newNum = endItem + "%";
    }
    const newEquaTion = _equation.slice(0, end).concat(newNum);
    setEquation(newEquaTion);
    setshowEquation(true);
  }

  const operatorList = OPERATOR_LIST.map((item) => (
    <div
      className="button"
      key={item.key}
      onClick={() => handleOperator(item.text)}
    >
      {item.text}
    </div>
  ));

  function handleOperator(operator) {
    if (operator === "=") {
      setshowEquation(isNaN(computedResult));
      setIsEval(true);
      return;
    }
    let _equation = [...equation];
    const { end, endItem, endWithOperator } = getEnd(_equation);
    // 前一位是运算符，替换
    if (endWithOperator) {
      setEquation(_equation.slice(0, end).concat(operator));
    } else {
      if (isEval) {
        if (isNaN(computedResult)) return;
        _equation = getEquation(_equation);
      }
      // 前一位是. 去掉
      if (/\.$/.test(endItem)) {
        _equation.splice(end, end + 1, endItem.replace(".", ""), operator);
      } else {
        _equation = _equation.concat(operator);
      }
      setEquation(_equation);
      setshowEquation(true);
    }
  }

  const computedResult = useMemo(() => {
    // 处理数字
    const _equation = equation.map((item) => {
      if (isOperator(item)) return item;
      // 以小数点开头的
      let resNum = item;
      if (/^\./.test(item)) {
        resNum = "0" + item;
      }
      if (item.includes("%")) {
        const num = resNum.replace("%", "");
        resNum = `${eval(num) / 100}`;
      }
      return resNum;
    });

    let equationStr = _equation.join("");
    // 替换乘除
    OPERATOR_MAP.forEach((value, key) => {
      const reg = new RegExp("\\" + key, "g");
      equationStr = equationStr.replace(reg, value);
    });
    try {
      eval(equationStr);
    } catch (error) {
      // 计算出错直接忽略当前输入的一位
      equationStr = equationStr.slice(0, equationStr.length - 1);
    }

    const resultObj = bignumber(evaluate(equationStr));
    if (isNaN(resultObj.e)) {
      return "除数不能为0";
    }
    const result = format(round(resultObj, 9));
    if (result.length <= 9 || resultObj.e === 0) return result;
    // 科学计数法
    const power = bignumber(resultObj.e);
    const powerStr = format(power);
    let leftNum = divide(resultObj, pow(bignumber(10), power));
    leftNum = round(leftNum, 8 - `${powerStr}`.length);
    const left = format(leftNum);
    return `${left}e${power}`;
  }, [equation]);

  return (
    <div className="calculator">
      <div className="display-area">
        <p
          className="equation"
          style={{ visibility: showEquation ? "visible" : "hidden" }}
        >
          {equation}
        </p>
        <p className="result">{computedResult}</p>
      </div>
      <div className="keyboard">
        <div className="left">
          <div className="action">{actionList}</div>
          <div className="num">{numList}</div>
        </div>
        <div className="right">{operatorList}</div>
      </div>
    </div>
  );
}
